package com.coolrui.domain.prometheus;

import cn.hutool.core.util.NumberUtil;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 自定义业务监控 - 订单金额指标服务
 * 支持多类型订单金额统计，指标单位：元（存储单位：分）
 */
@Component
public class AmountMetricsService {

    private static final Logger log = LoggerFactory.getLogger(AmountMetricsService.class);

    private final MeterRegistry meterRegistry;
    private final StringRedisTemplate redisTemplate;

    // 预定义统计类型常量
    public static final String TYPE_ALL = "all";
    public static final String TYPE_TEST = "test";

    // 所有支持的类型集合 - 直接初始化
    private final Set<String> supportedTypes = new HashSet<>(Arrays.asList(
            TYPE_ALL, TYPE_TEST
    ));

    // 本地缓存：类型 -> 金额（单位：分）
    private final ConcurrentMap<String, AtomicLong> orderAmountCache = new ConcurrentHashMap<>();

    // 本地缓存：类型 -> 上次更新时间（毫秒）
    private final ConcurrentMap<String, Long> lastUpdateTimeCache = new ConcurrentHashMap<>();

    // Redis key 前缀
    private static final String REDIS_ORDER_AMOUNT_KEY_PREFIX = "metrics:order:amount:cents:";

    // Micrometer 指标名称
    private static final String METRIC_NAME = "custom.order.amount.total";

    // 缓存有效期，超时后重新从 Redis 加载（降级用）
    private static final long CACHE_TTL_MS = 5000;

    public AmountMetricsService(MeterRegistry meterRegistry, StringRedisTemplate redisTemplate) {
        this.meterRegistry = meterRegistry;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 服务初始化 - 主动注册所有预定义类型
     */
    @PostConstruct
    public void init() {
        // 主动注册所有预定义类型
        for (String type : supportedTypes) {
            registerType(type);
        }
    }

    /**
     * 注册指定类型（主动注册模式）
     */
    public void registerType(String type) {
        if (type == null || type.trim().isEmpty()) {
            throw new IllegalArgumentException("类型不能为空");
        }

        // 初始化缓存
        orderAmountCache.putIfAbsent(type, new AtomicLong(0));
        lastUpdateTimeCache.putIfAbsent(type, 0L);

        // 从Redis加载初始值到本地缓存
        loadAmountFromRedis(type);

        // 注册Gauge指标
        registerGaugeForType(type);

        // 添加到支持类型集合
        supportedTypes.add(type);
    }

    /**
     * 从Redis加载指定类型的金额到本地缓存
     */
    private void loadAmountFromRedis(String type) {
        String redisKey = getRedisKey(type);
        try {
            String val = redisTemplate.opsForValue().get(redisKey);
            long currentAmount = 0L;

            if (val != null && !val.trim().isEmpty()) {
                try {
                    currentAmount = Long.parseLong(val);
                } catch (NumberFormatException e) {
                    log.error("Redis键 {} 的值格式错误: '{}'", redisKey, val);
                }
            }

            // 更新本地缓存
            AtomicLong cached = orderAmountCache.get(type);
            if (cached != null) {
                cached.set(currentAmount);
                lastUpdateTimeCache.put(type, System.currentTimeMillis());
            }
        } catch (Exception e) {
            log.warn("从Redis加载类型 {} 的金额失败: {}", type, e.getMessage());
        }
    }

    /**
     * 添加订单金额（默认类型为 all）
     */
    public void addOrderAmount(BigDecimal amountYuan) {
        addOrderAmount(amountYuan, TYPE_ALL);
    }

    /**
     * 添加订单金额（指定类型）
     */
    public void addOrderAmount(BigDecimal amountYuan, String type) {
        if (!isTypeSupported(type)) {
            log.warn("不支持的订单类型: {}, 自动注册该类型", type);
            registerType(type);
        }

        // 元转分
        long amountCents = NumberUtil.mul(amountYuan, 100)
                .setScale(0, RoundingMode.HALF_UP)
                .longValue();

        // 原子更新 Redis 中的金额
        String redisKey = getRedisKey(type);
        Long newTotalCents = redisTemplate.opsForValue().increment(redisKey, amountCents);

        if (newTotalCents != null) {
            // 设置或更新过期时间为30天
            redisTemplate.expire(redisKey, 30 * 24 * 60 * 60, TimeUnit.SECONDS);
            // 更新本地缓存
            AtomicLong cachedAmount = orderAmountCache.get(type);
            if (cachedAmount != null) {
                cachedAmount.set(newTotalCents);
                lastUpdateTimeCache.put(type, System.currentTimeMillis());
            }
        } else {
            log.error("Redis自增操作失败，类型: {}", type);
        }
    }

    /**
     * 获取某个类型的订单金额（单位：元）
     */
    public double getOrderAmount(String type) {
        if (!isTypeSupported(type)) {
            log.warn("获取未注册类型的金额: {}, 返回0", type);
            return 0.0;
        }

        // 检查缓存是否需要刷新
        refreshCacheIfNeeded(type);

        AtomicLong amountCents = orderAmountCache.get(type);
        return amountCents != null ? amountCents.get() / 100.0 : 0.0;
    }

    /**
     * 检查并刷新缓存（如果需要）
     */
    private void refreshCacheIfNeeded(String type) {
        Long lastUpdate = lastUpdateTimeCache.get(type);
        if (lastUpdate == null || System.currentTimeMillis() - lastUpdate > CACHE_TTL_MS) {
            loadAmountFromRedis(type);
        }
    }

    /**
     * 注册 Micrometer Gauge（按类型）
     */
    private void registerGaugeForType(String type) {
        Gauge.builder(METRIC_NAME, this, service -> service.getOrderAmount(type))
                .description("累计订单金额（元）")
                .tag("type", type)
                .register(meterRegistry);
    }

    /**
     * 检查类型是否已注册
     */
    public boolean isTypeSupported(String type) {
        return supportedTypes.contains(type);
    }

    /**
     * 获取 Redis 中对应类型的 key
     */
    private String getRedisKey(String type) {
        return REDIS_ORDER_AMOUNT_KEY_PREFIX + type;
    }
}